////////////////////////////////////////////////
// DESCRIPTION:
//     Bus Driver access for Device Drivers
//
// Legal Notices:
//     Copyright (c) 2008, Telliam Consulting, LLC.
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification,
//     are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//
//     * Neither the name of Telliam Consulting nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// Disclaimer:
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//     IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
//     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
//     OF THE POSSIBILITY OF SUCH DAMAGE.  
//

#include <Windows.h>
#include <defbus.h>
#include <FusionWare\DFX\StreamAPI.h>

using namespace FusionWare::DFX;
using namespace FusionWare::Win32;


bool BusDriver::SetDevicePowerState(const TCHAR* pDeviceName, CEDEVICE_POWER_STATE* pState) const
{
    if(!IsValid() || NULL==pDeviceName)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return false;
    }

    CE_BUS_POWER_STATE state = { const_cast<TCHAR*>(pDeviceName), pState };
    return IoControl(IOCTL_BUS_SET_POWER_STATE, &state, sizeof(state));
}

bool BusDriver::GetDevicePowerState(const TCHAR* pDeviceName, CEDEVICE_POWER_STATE* pState) const
{
    if(!IsValid() || NULL==pDeviceName)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return false;
    }

    CE_BUS_POWER_STATE state = { const_cast<TCHAR*>(pDeviceName), pState };
    // [Sic.] uses input buffer for output from the bus driver :-(
    return IoControl(IOCTL_BUS_SET_POWER_STATE, &state, sizeof(state));
}

bool BusDriver::SetDeviceConfigurationData(const TCHAR* pDeviceName, DWORD Space, ULONG BusNumber, ULONG SlotNumber, DWORD Offset, DWORD Length, void* pBuffer, DWORD* pActualOut) const
{
    // Is there a valid real bus for this device?
    if(IsValid() && pDeviceName != NULL )
    {
        CE_BUS_DEVICE_CONFIGURATION_DATA config = { const_cast<TCHAR*>(pDeviceName), Space, Offset, Length, pBuffer};

        if(!IoControl(IOCTL_BUS_SET_CONFIGURE_DATA, &config, sizeof(config)))
        {
            SetLastError(ERROR_GEN_FAILURE);
            return false;
        }
        
        *pActualOut = config.dwLength;
    }
    else // no "real" bus so fall back to "legacy" APIs
    {
        if( Space != PCI_WHICHSPACE_CONFIG && Space != PCCARD_PCI_CONFIGURATION_SPACE)
        {
            SetLastError(ERROR_INVALID_HANDLE);
            return false;
        }

        *pActualOut = HalSetBusDataByOffset(PCIConfiguration, BusNumber, SlotNumber, pBuffer, Offset, Length);
    }

    return true;
}

bool BusDriver::GetDeviceConfigurationData(const TCHAR* pDeviceName, DWORD Space, ULONG BusNumber, ULONG SlotNumber, DWORD Offset, DWORD Length, void* pBuffer, DWORD* pActualOut) const
{
    ASSERT(pActualOut!=NULL);
    
    *pActualOut = 0;

    if(IsValid()  && pDeviceName !=NULL )
    {
        CE_BUS_DEVICE_CONFIGURATION_DATA config = { const_cast<TCHAR*>(pDeviceName), Space, Offset, Length, pBuffer};

        if(!IoControl(IOCTL_BUS_SET_CONFIGURE_DATA, &config, sizeof(config)))
        {
            SetLastError(ERROR_INVALID_HANDLE);
            return false;
        }

        *pActualOut = config.dwLength;
    }
    else
    {
        if( Space != PCI_WHICHSPACE_CONFIG && Space != PCCARD_PCI_CONFIGURATION_SPACE)
        {
            SetLastError(ERROR_INVALID_HANDLE);
            return false;
        }
        
        *pActualOut = HalGetBusDataByOffset(PCIConfiguration, BusNumber,SlotNumber,pBuffer, Offset, Length);
    }

    return true;
}

bool BusDriver::IsChildRemoved(const TCHAR* pDeviceName, bool* pChildState) const
{
    if(!IsValid() || NULL==pDeviceName)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return false;
    }
    
    DWORD bytesReturned;
    size_t byteSize = (_tcslen(pDeviceName)+1)*sizeof(TCHAR);
    return IoControl(IOCTL_BUS_IS_CHILD_REMOVED, const_cast<TCHAR*>(pDeviceName), byteSize, pChildState, sizeof(*pChildState), &bytesReturned);
}

bool BusDriver::TranslateBusAddress(const TCHAR* pDeviceName, INTERFACE_TYPE  InterfaceType, ULONG BusNumber, PHYSICAL_ADDRESS BusAddress, ULONG* pInIoSpace, PHYSICAL_ADDRESS* pTranslatedAddress) const
{
    if(IsValid()  && pDeviceName!=NULL )
    {
        CE_BUS_TRANSLATE_BUS_ADDR busData = { const_cast<TCHAR*>(pDeviceName)
                                            , InterfaceType
                                            , BusNumber
                                            , BusAddress
                                            , pInIoSpace
                                            , pTranslatedAddress
                                            };


        if(IoControl(IOCTL_BUS_TRANSLATE_BUS_ADDRESS, &busData, sizeof(busData)))
            return true;
    }

    return HalTranslateBusAddress(InterfaceType, BusNumber, BusAddress, pInIoSpace, pTranslatedAddress) ? true : false;
}

bool BusDriver::TranslateSystemAddress(const TCHAR* pDeviceName, INTERFACE_TYPE  InterfaceType, ULONG BusNumber, PHYSICAL_ADDRESS BusAddress, PHYSICAL_ADDRESS* pTranslatedAddress) const
{
    if(IsValid()  && pDeviceName!=NULL )
    {
        CE_BUS_TRANSLATE_SYSTEM_ADDR busData = { const_cast<TCHAR*>(pDeviceName)
                                               , InterfaceType
                                               , BusNumber
                                               , BusAddress
                                               , pTranslatedAddress
                                               };


        if(IoControl(IOCTL_BUS_TRANSLATE_SYSTEM_ADDRESS, &busData, sizeof(busData)))
            return true;
    }

    return HalTranslateSystemAddress(InterfaceType, BusNumber, BusAddress, pTranslatedAddress) ? true : false;
}

bool BusDriver::TransAddrToVirtual(const TCHAR* pDeviceName, INTERFACE_TYPE InterfaceType, ULONG BusNumber, PHYSICAL_ADDRESS BusAddress, ULONG Length, ULONG* pInIoSpace, void** ppMappedAddress) const
{
    PHYSICAL_ADDRESS translatedAddr; 

    if(!TranslateBusAddress(pDeviceName, InterfaceType, BusNumber, BusAddress, pInIoSpace, &translatedAddr))
        return false;
    
    if(*pInIoSpace)
        *ppMappedAddress = (void*)translatedAddr.LowPart;
     else
        *ppMappedAddress = MmMapIoSpace(translatedAddr, Length, FALSE);
    
    return (*ppMappedAddress!=NULL);
}

bool BusDriver::TransAddrToStatic(const TCHAR* pDeviceName, INTERFACE_TYPE InterfaceType, ULONG BusNumber, PHYSICAL_ADDRESS BusAddress, ULONG Length, ULONG* pInIoSpace, void** ppMappedAddress) const
{
    PHYSICAL_ADDRESS translatedAddr; 
    
    if(NULL==ppMappedAddress)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return false;
    }

    if(!TranslateBusAddress(pDeviceName, InterfaceType, BusNumber, BusAddress, pInIoSpace, &translatedAddr))
        return false;
    
    if(*pInIoSpace)
        *ppMappedAddress = (void*)translatedAddr.LowPart;
    else
    {
        // Memory-mapped I/O, get statically-mapped virtual address for translated physical address
        // Page align source and adjust size to compensate
        DWORD AlignedAddr = translatedAddr.LowPart & ~(PAGE_SIZE - 1);
        DWORD AlignedSize = Length + (translatedAddr.LowPart & (PAGE_SIZE - 1));

        *ppMappedAddress = CreateStaticMapping(AlignedAddr >> 8, AlignedSize);
        
        if(*ppMappedAddress)
            *((BYTE **)ppMappedAddress) += (translatedAddr.LowPart & (PAGE_SIZE - 1));
    }

    return *ppMappedAddress!=NULL;
}


